﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using log4net;
using TeamCommunitySolution.Domain.DataLayer.TeamEvents;
using TeamCommunitySolution.Modules.TeamEvents.Requests;
using TeamCommunitySolution.Modules.TeamEvents.Responses;
using TeamCommunitySolution.Repositories.Base;
using TeamCommunitySolution.Repositories.TeamEvents;
using TeamCommunitySolution.Modules.Shared;
using TeamCommunitySolution.Modules.Messaging;
using TeamCommunitySolution.Domain.DataLayer.Base;

namespace TeamCommunitySolution.Modules.TeamEvents
{
    /// <summary>
    /// </summary>
    public class TeamEventManagerImpl : TeamEventManager
    {
        /// <summary>
        /// The logger instance.
        /// </summary>
        private static readonly ILog _log = LogManager.GetLogger(typeof(TeamEventManagerImpl));

        /// <summary>
        /// The repository reference.
        /// </summary>
        private TeamEventRepository teamEventRepo;

        private TeamEventManagerImpl()
        {
        }

        /// <summary>
        /// Creates the instance.
        /// </summary>
        /// <param name="teamEventRepo">The team event repo.</param>
        /// <returns>Team Event Manager instance.</returns>
        public static TeamEventManager CreateInstance(TeamEventRepository teamEventRepo)
        {
            TeamEventManagerImpl output = new TeamEventManagerImpl();
            output.teamEventRepo = teamEventRepo;

            return output;
        }

        /// <summary>
        /// Creates the team event.
        /// </summary>
        /// <param name="request">The request.</param>
        /// <returns>Team Event Response.</returns>
        public TeamEventResponse CreateTeamEvent(TeamEventCreateRequest request)
        {
            this.VerifyRequest(request);

            TeamEventResponse response = new TeamEventResponse();

            try
            {
                TeamEvent teamEvent = new TeamEvent
                                          {
                                              Description = request.Description,
                                              EndDate = request.EndDate,
                                              Name = request.EventName,
                                              Owner = request.Owner,
                                              StartDate = request.StartDate
                                          };

                this.teamEventRepo.SaveTeamEvent(ref teamEvent);

                response.TeamEvent = teamEvent;
                response.Status = OperationStatus.Success;
            }
            catch (TeamEventRepositoryException e)
            {
                response.Status = OperationStatus.Failure;
                response.Message = string.Format("Incomplete team event create for request {0} : {1}", request.EventName, e.Message);
                response.Exception = e;
                _log.Error(response.Message, e);
            }

            return response;
        }

        /// <summary>
        /// Updates the team event.
        /// </summary>
        /// <param name="request">The request.</param>
        /// <returns>Teamo Event Response.</returns>
        public TeamEventResponse UpdateTeamEvent(TeamEventUpdateRequest request)
        {
            if (String.IsNullOrEmpty(request.Id))
            {
                throw new InvalidTeamEventCreateRequestException("The team event update requires an id.");
            }

            this.VerifyRequest(request);

            TeamEventResponse response = new TeamEventResponse();

            try
            {
                TeamEvent teamEvent = new TeamEvent
                {
                    Id = request.Id,
                    Description = request.Description,
                    EndDate = request.EndDate,
                    Name = request.EventName,
                    Owner = request.Owner,
                    StartDate = request.StartDate
                };

                this.teamEventRepo.SaveTeamEvent(ref teamEvent);

                response.TeamEvent = teamEvent;
                response.Status = OperationStatus.Success;
            }
            catch (TeamEventRepositoryException e)
            {
                response.Status = OperationStatus.Failure;
                response.Message = string.Format("Incomplete team event update for request {0} : {1}", request.EventName, e.Message);
                response.Exception = e;
                _log.Error(response.Message, e);
            }

            return response;
        }

        /// <summary>
        /// Sends the specified invite request.
        /// </summary>
        /// <param name="inviteRequest">The invite request.</param>
        /// <returns>Team Invite Response.</returns>
        public TeamEventInviteResponse Send(TeamEventInviteRequest inviteRequest)
        {
            if (inviteRequest == null)
            {
                throw new InvalidTeamEventException("The invite request cannot be null.");
            }

            if (inviteRequest.InvitationCount < 1)
            {
                throw new InvalidTeamEventException("The invite request needs invitees.");
            }

            if (inviteRequest.TeamEvent == null)
            {
                throw new InvalidTeamEventException("The event in the invite cannot be null");
            }

            if (TeamEventRepositoryImpl.CreateInstance().GetTeamEventByField("Id", inviteRequest.TeamEvent.Id) == null)
            {
                throw new InvalidTeamEventException("The event specified in the invite does not exist");
            }

            if (BaseRepositoryImpl.CreateInstance().GetUserByField("Id", inviteRequest.Owner.Id) == null)
            {
                throw new InvalidTeamEventCreateRequestException("The team event owner specified does not exist.");
            }

            TeamEventInviteResponse response = new TeamEventInviteResponse();

            try
            {
                for (int i = 0; i < inviteRequest.InvitationCount; i++)
                {
                    TeamEventInvite invite = new TeamEventInvite
                                                 {
                                                     Event = inviteRequest.TeamEvent,
                                                     FromUser = inviteRequest.Owner,
                                                     RsvpStatus = false,
                                                     ToUser = inviteRequest.GetInvitee(i)
                                                 };
                    this.teamEventRepo.SaveTeamEventInvite(invite);
                }

                response.Status = OperationStatus.Success;
            }
            catch (TeamEventRepositoryException e)
            {
                response.Status = OperationStatus.Failure;
                response.Message = string.Format("Incomplete team event invite send for request {0} : {1}", inviteRequest.TeamEvent.Name, e.Message);
                response.Exception = e;
                _log.Error(response.Message, e);
            }

            return response;
        }

        /// <summary>
        /// Gets the invite.
        /// </summary>
        /// <param name="request">The request.</param>
        /// <returns>Team Event Invite Response.</returns>
        public TeamEventInviteResponse GetInvite(TeamEventInviteViewRequest request)
        {
            if (request == null)
            {
                throw new InvalidTeamEventException("The request cannot be null.");
            }

            if (string.IsNullOrEmpty(request.InviteId))
            {
                throw new InvalidTeamEventException("The id for the invite cannot be empty");
            }

            TeamEventInviteResponse response = new TeamEventInviteResponse();

            try
            {
                response.Invite = this.teamEventRepo.GetTeamEventInviteByField("Id", request.InviteId);
                response.Status = OperationStatus.Success;
            }
            catch (Exception e)
            {
                response.Status = OperationStatus.Failure;
                response.Message = string.Format("Incomplete team event invite view for request {0} : {1}", request.InviteId, e.Message);
                response.Exception = e;
                _log.Error(response.Message, e);
            }

            return response;
        }

        /// <summary>
        /// Gets the team event.
        /// </summary>
        /// <param name="request">The request.</param>
        /// <returns>Team Event Response</returns>
        public TeamEventResponse GetTeamEvent(TeamEventRequest request)
        {
            if (request == null)
            {
                throw new InvalidTeamEventCreateRequestException("The request cannot be null.");
            }

            if (String.IsNullOrEmpty(request.TeamEventId))
            {
                throw new InvalidTeamEventCreateRequestException("The team event request requires an Id.");
            }

            TeamEventResponse response = new TeamEventResponse();

            try
            {
                response.TeamEvent = this.teamEventRepo.GetTeamEventByField("Id", request.TeamEventId);
                response.Status = OperationStatus.Success;
            }
            catch (Exception e)
            {
                response.Status = OperationStatus.Failure;
                response.Message = string.Format("Could not find the team event for request {0} : {1}", request.TeamEventId, e.Message);
                response.Exception = e;
                _log.Error(response.Message, e);
            }

            return response;
        }

        /// <summary>
        /// Events the invite accept.
        /// </summary>
        /// <param name="request">The request.</param>
        /// <returns>Team Event Invite Response.</returns>
        public TeamEventInviteResponse EventInviteAccept(TeamEventAcceptInviteRequest request)
        {
            if (request == null)
            {
                throw new InvalidTeamEventCreateRequestException("The request cannot be null.");
            }

            if (String.IsNullOrEmpty(request.EventInviteId))
            {
                throw new InvalidTeamEventCreateRequestException("The team event invite requires an id.");
            }

            TeamEventInviteResponse response = new TeamEventInviteResponse();

            try
            {
                TeamEventInvite invite = this.teamEventRepo.GetTeamEventInviteByField("Id", request.EventInviteId);
                invite.RsvpStatus = true;
                this.teamEventRepo.SaveTeamEventInvite(invite);
                response.Invite = invite;
                response.Status = OperationStatus.Success;
            }
            catch (Exception e)
            {
                response.Status = OperationStatus.Failure;
                response.Message = string.Format("Could not update team event invite for request {0} : {1}", request.EventInviteId, e.Message);
                response.Exception = e;
                _log.Error(response.Message, e);
            }

            return response;
        }

        /// <summary>
        /// Events the invite accept.
        /// </summary>
        /// <param name="request">The request.</param>
        /// <returns>Team Event Invite Response.</returns>
        public TeamEventInviteResponse EventInviteDecline(TeamEventDeclineInviteRequest request)
        {
            if (request == null)
            {
                throw new InvalidTeamEventCreateRequestException("The request cannot be null.");
            }

            if (String.IsNullOrEmpty(request.EventInviteId))
            {
                throw new InvalidTeamEventCreateRequestException("The team event invite requires an id.");
            }

            TeamEventInviteResponse response = new TeamEventInviteResponse();

            try
            {
                TeamEventInvite invite = this.teamEventRepo.GetTeamEventInviteByField("Id", request.EventInviteId);
                invite.RsvpStatus = false;
                this.teamEventRepo.SaveTeamEventInvite(invite);
                response.Invite = invite;
                response.Status = OperationStatus.Success;
            }
            catch (Exception e)
            {
                response.Status = OperationStatus.Failure;
                response.Message = string.Format("Could not update team event invite for request {0} : {1}", request.EventInviteId, e.Message);
                response.Exception = e;
                _log.Error(response.Message, e);
            }

            return response;
        }

        /// <summary>
        /// Verifies the request.
        /// </summary>
        /// <param name="request">The request.</param>
        private void VerifyRequest(TeamEventCreateRequest request)
        {
            if (request == null)
            {
                throw new InvalidTeamEventCreateRequestException("The request cannot be null.");
            }

            if (String.IsNullOrEmpty(request.EventName))
            {
                throw new InvalidTeamEventCreateRequestException("The team event requires a name.");
            }

            if (request.StartDate.Equals(DateTime.MinValue))
            {
                throw new InvalidTeamEventCreateRequestException("The team event requires a valid start date.");
            }

            if (request.EndDate.Equals(DateTime.MinValue))
            {
                throw new InvalidTeamEventCreateRequestException("The team event requires a valid end date.");
            }

            if (request.StartDate > request.EndDate)
            {
                throw new InvalidTeamEventCreateRequestException("The team event start date cannot be after the end date.");
            }

            if (request.Owner == null)
            {
                throw new InvalidTeamEventCreateRequestException("The team event owner cannot be null.");
            }

            if (BaseRepositoryImpl.CreateInstance().GetUserByField("Id", request.Owner.Id) == null)
            {
                throw new InvalidTeamEventCreateRequestException("The team event owner specified does not exist.");
            }
        }
    }
}
